<script setup>
import { computed, watch, inject, ref } from 'vue';
import { REQU, toFormData, useResp } from '@/net/requ.js';
import { message } from 'ant-design-vue';
import * as storage from '@/util/storage.js';

const userInfo = storage.readAsObj('userInfo');
const props = defineProps(['visible', 'role']);
const emit = defineEmits(['update:visible', 'ok']);
const menuList = inject('menuList');

const visible = ref(props.visible);
watch(visible, value => emit('update:visible', value));

const codesFromFuncList = R.compose( R.map(R.prop('functionCode')), R.prop('funcList') );

const genCodesMap = (genCodes, xs) => R.compose(
  R.fromPairs,
  R.map(R.juxt( [R.prop('id'), R.compose( R.constructN(1, Set), genCodes )] ))
)(xs);

const codesMap = computed(() => genCodesMap(codesFromFuncList, menuList.value));
const sending = ref(false);
const formElem = ref();

const formState = ref({
  orgId: R.propOr('', 'orgId', userInfo),
  id: props.role.id,
  code: R.mergeDeepRight(
    genCodesMap(R.always([]), menuList.value),
    genCodesMap(codesFromFuncList, props.role.bindMenuVOList)
  ),
});

const functionsCodes = computed(() => (
  R.compose(
    R.join(','),
    R.map(R.compose( R.join(','), Array.from )),
    R.filter(R.compose( R.gt(R.__, 0), R.prop('size') )),
    R.values
  )(formState.value.code)
));

function handleChange(menuId, event, v) {
  const action = event.target.checked ? 'add' : 'delete';
  const functionCodes = 'all' == v ? Array.from(codesMap.value[menuId]) : [v];
  functionCodes.forEach(code => formState.value.code[menuId][action](code));
}

function save() {
  sending.value = true;

  return REQU('/api/role/bindMenuFunc', {
    method: 'POST',
    body: toFormData(Object.assign(
      R.omit(['code'], formState.value),
      { functionsCodes: functionsCodes.value },
    ))
  })
  .then(useResp({
    succ: result => {
      message.success('保存成功');
      return result;
    },
    fail: result => {
      message.error(result.message || '保存失败');
      return result;
    },
  }))
  .finally(() => sending.value = false);
}

function onOk() {
  formElem.value
  .validate()
  .then(save)
  .then(result => {
    if (result.code) {
      emit('ok');
      visible.value = false;
    }
  })
  .catch();
}
</script>

<template>
  <a-modal
    :visible="visible"
    :width="970"
    centered
    class="perms-modal"
    :title="`${ formState.id ? '编辑' : '新建' }角色权限`"
    @cancel="visible = false"
    @ok="onOk">
    <a-form ref="formElem" :model="formState">
      <a-form-item>
        <template v-for="(menu, i) in menuList" :key="menu.id">
          <a-checkbox
            class="all"
            :checked="formState.code[menu.id]?.size == codesMap[menu.id].size"
            :indeterminate="0 < formState.code[menu.id]?.size && formState.code[menu.id].size < codesMap[menu.id].size"
            @change="event => handleChange(menu.id, event, 'all')">
            {{ menu.menuName }}
          </a-checkbox>

          <div class="checkbox-group">
            <template v-for="func in menu.funcList" :key="`${menu.id}-${func.id}`">
              <a-checkbox
                :checked="formState.code[menu.id]?.has(func.functionCode)"
                @change="event => handleChange(menu.id, event, func.functionCode)">
                {{ func.functionName }}
              </a-checkbox>
            </template>
          </div>
        </template>
      </a-form-item>
    </a-form>
  </a-modal>
</template>

<style>
.perms-modal .ant-modal-body {
  overflow-y: auto;
  height: 340px;
}

.perms-modal .ant-form-item {
  column-gap: .5em;
  align-items: baseline;
}

.perms-modal .ant-checkbox-wrapper + .ant-checkbox-wrapper { margin: 0; }

.perms-modal .ant-checkbox-wrapper.all {
  width: 100%;
  height: 32px;
  line-height: 32px;
  padding-left: 16px;
  background: rgb(240,240,240);
}

.perms-modal .checkbox-group {
  display: flex;
  row-gap: 24px;
  flex-wrap: wrap;
  padding: 24px 16px;
  margin-bottom: 1em;
}

.perms-modal .checkbox-group > * { width: 20%; }
</style>
